﻿using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.Serialization;

namespace CmdTailCmd
{
    /// <summary>
    /// Base class for all exceptions specific to the CmdTailCmd library
    /// </summary>
    [Serializable] public class CmdTailException : Exception
    {
        private static string ErrorText() {return "Error parsing command tail";}

        [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] // Just not really worrying about it in this case.
        public string[] CommandTailTokens {get; set;}
        public string   Token             {get; set;}
        public string   SettingName       {get; set;}
        public string   SettingValue      {get; set;}
        public Type     SettingType       {get; set;}

        public CmdTailException(string[] tokens)                  : base(ErrorText())         {CommandTailTokens = tokens;}
        public CmdTailException(string[] tokens, Exception inner) : base(ErrorText(), inner)  {CommandTailTokens = tokens;}
        
        public CmdTailException() {}
        public CmdTailException(string message)                  : base(message)        {}
        public CmdTailException(string message, Exception inner) : base(message, inner) {}
        
        protected CmdTailException(System.Runtime.Serialization.SerializationInfo info, 
                                   System.Runtime.Serialization.StreamingContext context)
            : base(info, context) 
        {
            Token             = info.GetString("Token");
            SettingName       = info.GetString("SettingName");
            SettingValue      = info.GetString("SettingValue");
            SettingType       = info.GetValue("SettingType",       typeof(Type))     as Type;
            CommandTailTokens = info.GetValue("CommandTailTokens", typeof(string[])) as string[];
        }

        public override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("Token",              Token);
            info.AddValue("SettingName",        SettingName);
            info.AddValue("SettingValue",       SettingValue);
            info.AddValue("SettingType",        SettingType);           
            info.AddValue("CommandTailTokens",  CommandTailTokens);

            base.GetObjectData(info, context);
        }
        
    }

    /// <summary>
    /// Empty token. Either an empty string was passed in the constructor array or one token
    /// consists of nothing other than a switchchar
    /// </summary>
    [Serializable] public class EmptyTokenException : CmdTailException
    {
        private static string ErrorText() {return "Empty command token found";}

        public EmptyTokenException() : base(ErrorText()) {}
        public EmptyTokenException(string message) : base(message) {}
        public EmptyTokenException(string message, Exception inner) : base(message, inner) {}
        protected EmptyTokenException(
          System.Runtime.Serialization.SerializationInfo info,
          System.Runtime.Serialization.StreamingContext context)
            : base(info, context) {}
    }

    /// <summary>
    /// The token was semantically invalid, for example "/b+=true" or "/=value" or "/12"
    /// </summary>
    [Serializable] public class MalformedTokenException : CmdTailException
    {
        private static string ErrorText(string TokenName) {return "Unrecognized command line token '" + TokenName + "'";}

        public MalformedTokenException() {}
        public MalformedTokenException(string TokenName) : base(ErrorText(TokenName)) {}
        public MalformedTokenException(string TokenName, Exception inner) : base(ErrorText(TokenName), inner) {}
        protected MalformedTokenException(
          System.Runtime.Serialization.SerializationInfo info,
          System.Runtime.Serialization.StreamingContext context)
            : base(info, context) {}
    }

    /// <summary>
    /// A switch character was passed with no token name and it can't be matched to a special case
    /// </summary>
    [Serializable] public class UnNamedTokenException : MalformedTokenException
    {
        private static string ErrorText = "Unexpected switch character passed with no token name";
        public UnNamedTokenException() { }
        public UnNamedTokenException(string message) : base(ErrorText + " message: " + message) {}
        public UnNamedTokenException(string message, Exception inner) : base(ErrorText + " message: " + message, inner) { }
        protected UnNamedTokenException(
          System.Runtime.Serialization.SerializationInfo info,
          System.Runtime.Serialization.StreamingContext context)
            : base(info, context) { }
    }

    /// <summary>
    /// A token could match more than one field in the target object
    /// </summary>
    [Serializable] public class AmbiguousSettingNameException : CmdTailException
    {
        private static string ErrorText(string SettingName, IEnumerable<string> PossibleMatches) 
        {
            return "Ambiguous parameter '" + SettingName + "'. Possible matches: " + string.Join(", ", PossibleMatches);
        }

        public AmbiguousSettingNameException() {}
        public AmbiguousSettingNameException(string SettingName, IEnumerable<string> PossibleMatches) 
            : base(ErrorText(SettingName, PossibleMatches)) {}
        public AmbiguousSettingNameException(string SettingName, IEnumerable<string> PossibleMatches, Exception inner) 
            : base(ErrorText(SettingName, PossibleMatches), inner) {}
        public AmbiguousSettingNameException(string message) : base(message) {}
        public AmbiguousSettingNameException(string message, Exception inner) : base(message, inner) {}
        protected AmbiguousSettingNameException(
          System.Runtime.Serialization.SerializationInfo info,
          System.Runtime.Serialization.StreamingContext context)
            : base(info, context) {}
    }

    /// <summary>
    /// A token was found that doesn't match any field in the target object
    /// You can ignore this exception by setting the property IgnoreUnknownParameters
    /// in the parser
    /// </summary>
    [Serializable] public class UnknownSettingException : CmdTailException
    {
        private static string ErrorText(string SettingName) {return "Unknown setting '" + SettingName + "'";}

        public UnknownSettingException() {}
        public UnknownSettingException(string SettingNameArg) : base(ErrorText(SettingNameArg)) {SettingName = SettingNameArg;}
        public UnknownSettingException(string SettingNameArg, Exception inner) : base(ErrorText(SettingNameArg), inner) {SettingName = SettingNameArg;}
        protected UnknownSettingException(
          System.Runtime.Serialization.SerializationInfo info,
          System.Runtime.Serialization.StreamingContext context)
            : base(info, context) {}
    }

    /// <summary>
    /// A token was found that should require a value (for example, an int or a bool) but
    /// no appropriate value can be found
    /// </summary>
    [Serializable] public class SettingMissingValueException : CmdTailException
    {
        private static string ErrorText(string SettingName) {return "Setting '" + SettingName + "' requires a value";}
        
        public SettingMissingValueException() {}
        public SettingMissingValueException(string SettingName) : base(ErrorText(SettingName)) {}
        public SettingMissingValueException(string SettingName, Exception inner) : base(ErrorText(SettingName), inner) {}
        protected SettingMissingValueException(
          System.Runtime.Serialization.SerializationInfo info,
          System.Runtime.Serialization.StreamingContext context)
            : base(info, context) {}
    }

    /// <summary>
    /// The an enumeration value cannot be matched to any value in the enumeration
    /// </summary>
    [Serializable] public class UnrecognizedValueException : CmdTailException
    {
        private static string ErrorText(string SettingName, string Value)
        {
            return "Unrecognized value '" + Value + "' for setting '" + SettingName + "'";
        }

        public UnrecognizedValueException() {}
        public UnrecognizedValueException(string message) : base(message) {}
        public UnrecognizedValueException(string SettingName, string Value) : base(ErrorText(SettingName, Value)) {}
        public UnrecognizedValueException(string SettingName, string Value, Exception inner) : base(ErrorText(SettingName, Value), inner) {}
        public UnrecognizedValueException(string message, Exception inner) : base(message, inner) {}
        protected UnrecognizedValueException(
          System.Runtime.Serialization.SerializationInfo info,
          System.Runtime.Serialization.StreamingContext context)
            : base(info, context) {}
    }

    /// <summary>
    /// An enumeration value could match more than one value in the enumeration
    /// </summary>
    [Serializable] public class AmbiguousSettingValueException : CmdTailException
    {
        private static string ErrorText(string SettingName, string SettingValue, IEnumerable<string> MatchingValues)
        {
            return "Value '" + SettingValue + "' for setting '"  + SettingName + "' is ambiguous. Possible matches: " + String.Join(", ", MatchingValues);
        }

        public AmbiguousSettingValueException() {}
        public AmbiguousSettingValueException(string message) : base(message) {}
        public AmbiguousSettingValueException(string SettingName, string SettingValue, IEnumerable<string> MatchingValues)
            : base(ErrorText(SettingName, SettingValue, MatchingValues)) {}
        public AmbiguousSettingValueException(string SettingName, string SettingValue, IEnumerable<string> MatchingValues, Exception inner)
            : base(ErrorText(SettingName, SettingValue, MatchingValues), inner) {}
        public AmbiguousSettingValueException(string message, Exception inner) : base(message, inner) {}
        protected AmbiguousSettingValueException(
          System.Runtime.Serialization.SerializationInfo info,
          System.Runtime.Serialization.StreamingContext context)
            : base(info, context) {}
    }

    /// <summary>
    /// The value cannot be assigned to the target field
    /// </summary>
    [Serializable] public class InvalidSettingValueException : CmdTailException
    {
        private static string ErrorText(string SettingName, string SettingValue, Type SettingType)
        {
            return "'" + SettingValue + "' is not a valid for the setting '" + SettingName + "'. The value must be a " + SettingType.Name;
        }

        public InvalidSettingValueException() {}
        public InvalidSettingValueException(string SettingName, string SettingValue, Type SettingType)
            : base(ErrorText(SettingName, SettingValue, SettingType)) {}
        public InvalidSettingValueException(string SettingName, string SettingValue, Type SettingType, Exception inner)
            : base(ErrorText(SettingName, SettingValue, SettingType), inner) {}
        public InvalidSettingValueException(string message) : base(message) {}
        public InvalidSettingValueException(string message, Exception inner) : base(message, inner) {}
        protected InvalidSettingValueException(
          System.Runtime.Serialization.SerializationInfo info,
          System.Runtime.Serialization.StreamingContext context)
            : base(info, context) {}
    }
}
